/*******************************************************************************
 * Copyright (c) 2000, 2004 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
module dwt.accessibility.accessible;


private import dwt.dwt;


private import dwt.accessibility.acc;
private import dwt.accessibility.accevents;

private import dwt.widgets.control;
private import dwt.widgets.table;
private import dwt.widgets.tableitem;
private import dwt.widgets.tree;

private import dwt.ole.win32.ole;
private import dwt.ole.win32.variant;
private import dwt.internal.ole.win32.com;
private import dwt.internal.dwteventlistener;
private import dwt.util.vector;
private import dwt.util.javatypes;
private import dwt.internal.win32.wintypes;
private import dwt.internal.ole.win32.ifs;
private import dwt.internal.ole.win32.OAIDL;
private import dwt.internal.ole.win32.extras;


class _IAccessibleImpl : IAccessible {

	private import std.c.windows.windows;
	private import std.c.windows.com;

	Accessible	parent;
	this(Accessible	p) { parent = p; }
extern (Windows):
	// interface of IUnknown
	HRESULT QueryInterface(REFIID riid, void ** ppvObject) { return parent.QueryInterface(riid, ppvObject); }
	ULONG AddRef()	{ return parent.AddRef(); }
	ULONG Release()	{ return parent.Release(); }

	// interface of IDispatch
	HRESULT GetTypeInfoCount(UINT * pctinfo) { return COM.E_NOTIMPL; }
	HRESULT GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo * ppTInfo) { return COM.E_NOTIMPL; }
	HRESULT GetIDsOfNames(REFIID riid, LPOLESTR * rgszNames, UINT cNames, LCID lcid, DISPID * rgDispId) { return COM.E_NOTIMPL; }
	HRESULT Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS* pDispParams,VARIANT* pVarResult,EXCEPINFO* pExcepInfo,UINT* puArgErr) { return COM.E_NOTIMPL; }

	// interface of IAccessible
	HRESULT get_accParent(LPDISPATCH * ppdispParent) { return parent.get_accParent(ppdispParent); }
	HRESULT get_accChildCount(LONG* pcountChildren) { return parent.get_accChildCount(pcountChildren); }
	HRESULT get_accChild(VARIANT varChildID, LPDISPATCH* ppdispChild) {
		return parent.get_accChild(varChildID, ppdispChild);
	}
	HRESULT get_accName(VARIANT varID, BSTR* pszName) {
		return parent.get_accName(varID, pszName);
	}
	HRESULT get_accValue(VARIANT varID, BSTR* pszValue) {
		return parent.get_accValue(varID, pszValue);
	}
	HRESULT get_accDescription(VARIANT varID,BSTR* pszDescription) {
		return parent.get_accDescription(varID, pszDescription);
	}
	HRESULT get_accRole(VARIANT varID, VARIANT* pvarRole) {
		return parent.get_accRole(varID, pvarRole);
	}
	HRESULT get_accState(VARIANT varID, VARIANT* pvarState) {
		return parent.get_accState(varID, pvarState);
	}
	HRESULT get_accHelp(VARIANT varID, BSTR* pszHelp) {
		return parent.get_accHelp(varID, pszHelp);
	}
	HRESULT get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild, LONG* pidTopic) {
		return parent.get_accHelpTopic(pszHelpFile, varChild, pidTopic);
	}
	HRESULT get_accKeyboardShortcut(VARIANT varID, BSTR* pszKeyboardShortcut) {
		return parent.get_accKeyboardShortcut(varID, pszKeyboardShortcut);
	}
	HRESULT get_accFocus(VARIANT* pvarID) { return parent.get_accFocus(pvarID); }
	HRESULT get_accSelection(VARIANT* pvarChildren) { return parent.get_accSelection(pvarChildren); }
	HRESULT get_accDefaultAction(VARIANT varID,BSTR* pszDefaultAction) {
		return parent.get_accDefaultAction(varID, pszDefaultAction);
	}
	HRESULT accSelect(LONG flagsSelect, VARIANT varID) {
		return parent.accSelect(flagsSelect, varID);
	}
	HRESULT accLocation(LONG* pxLeft, LONG* pyTop, LONG* pcxWidth, LONG* pcyHeight, VARIANT varID) {
		return parent.accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varID);
	}
	HRESULT accNavigate(LONG navDir, VARIANT varStart, VARIANT* pvarEnd) {
		return parent.accNavigate(navDir, varStart, pvarEnd);
	}
	HRESULT accHitTest(LONG xLeft,  LONG yTop, VARIANT* pvarID) {
		return parent.accHitTest(xLeft, yTop, pvarID);
	}
	HRESULT accDoDefaultAction(VARIANT varID) {
		return parent.accDoDefaultAction(varID);
	}
	HRESULT put_accName(VARIANT varID, BSTR* szName) {
		return parent.put_accName(varID, szName);
	}
	HRESULT put_accValue(VARIANT varID, BSTR* szValue) {
		return parent.put_accValue(varID, szValue);
	}
}

class _IEnumVARIANTImpl : IEnumVARIANT {

	private import std.c.windows.windows;
	private import std.c.windows.com;

	Accessible	parent;
	this(Accessible	a) { parent = a; }
extern (Windows):
	// interface of IUnknown
	HRESULT QueryInterface(REFIID riid, void ** ppvObject) { return parent.QueryInterface(riid, ppvObject); }
	ULONG AddRef()	{ return parent.AddRef(); }
	ULONG Release()	{ return parent.Release(); }

	// interface of IEnumVARIANT
	HRESULT Next(ULONG celt, VARIANT *rgvar, ULONG *pceltFetched) { return parent.Next(celt, rgvar, pceltFetched); }
	HRESULT Skip(ULONG celt) { return parent.Skip(celt); }
	HRESULT Reset() { return parent.Reset(); }
	HRESULT Clone(LPENUMVARIANT * ppenum) { return COM.E_NOTIMPL;}
}

/**
 * Instances of this class provide a bridge between application
 * code and assistive technology clients. Many platforms provide
 * default accessible behavior for most widgets, and this class
 * allows that default behavior to be overridden. Applications
 * can get the default Accessible object for a control by sending
 * it <code>getAccessible</code>, and then add an accessible listener
 * to override simple items like the name and help string, or they
 * can add an accessible control listener to override complex items.
 * As a rule of thumb, an application would only want to use the
 * accessible control listener to implement accessibility for a
 * custom control.
 *
 * @see Control#getAccessible
 * @see AccessibleListener
 * @see AccessibleEvent
 * @see AccessibleControlListener
 * @see AccessibleControlEvent
 *
 * @since 2.0
 */
public class Accessible  {

	private import std.c.windows.windows;
	private import std.c.windows.com;
	private import tango.core.Thread;


	int refCount = 0, enumIndex = 0;
	_IAccessibleImpl objIAccessible;
	_IEnumVARIANTImpl objIEnumVARIANT;

	IAccessible iaccessible;
	Vector!(DWTEventListener) accessibleListeners ;
	Vector!(DWTEventListener) accessibleControlListeners;
	Vector!(DWTEventListener) textListeners;
	Object[] variants;
	Control control;

	this(Control control) {
		accessibleListeners = new Vector!(DWTEventListener);
		accessibleControlListeners = new Vector!(DWTEventListener);
		textListeners = new Vector!(DWTEventListener);

		this.control = control;

		HRESULT result = COM.CreateStdAccessibleObject(control.handle, COM.OBJID_CLIENT, &COM.IIDIAccessible, cast(void**)&iaccessible);
		if (result == COM.E_NOTIMPL) return;
		if (result != COM.S_OK) OLE.error(__FILE__, __LINE__, OLE.ERROR_CANNOT_CREATE_OBJECT, result);
		iaccessible.AddRef();

		objIAccessible = new _IAccessibleImpl(this);
		objIEnumVARIANT = new _IEnumVARIANTImpl(this) ;
		AddRef();
	}

	/**
	 * Invokes platform specific functionality to allocate a new accessible object.
	 * <p>
	 * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
	 * API for <code>Accessible</code>. It is marked public only so that it
	 * can be shared within the packages provided by DWT. It is not
	 * available on all platforms, and should never be called from
	 * application code.
	 * </p>
	 *
	 * @param control the control to get the accessible object for
	 * @return the platform specific accessible object
	 */
	public static Accessible internal_new_Accessible(Control control) {
		return new Accessible(control);
	}

	/**
	 * Adds the listener to the collection of listeners who will
	 * be notifed when an accessible client asks for certain strings,
	 * such as name, description, help, or keyboard shortcut. The
	 * listener is notified by sending it one of the messages defined
	 * in the <code>AccessibleListener</code> interface.
	 *
	 * @param listener the listener that should be notified when the receiver
	 * is asked for a name, description, help, or keyboard shortcut string
	 *
	 * @exception IllegalArgumentException <ul>
	 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
	 * </ul>
	 * @exception SWTException <ul>
	 *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
	 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
	 * </ul>
	 *
	 * @see AccessibleListener
	 * @see #removeAccessibleListener
	 */
	public void addAccessibleListener(AccessibleListener listener) {
		checkWidget();
		if (listener is null) DWT.error(__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
		accessibleListeners.addElement(listener);
	}


	/**
	 * Adds the listener to the collection of listeners who will
	 * be notifed when an accessible client asks for custom control
	 * specific information. The listener is notified by sending it
	 * one of the messages defined in the <code>AccessibleControlListener</code>
	 * interface.
	 *
	 * @param listener the listener that should be notified when the receiver
	 * is asked for custom control specific information
	 *
	 * @exception IllegalArgumentException <ul>
	 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
	 * </ul>
	 * @exception SWTException <ul>
	 *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
	 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
	 * </ul>
	 *
	 * @see AccessibleControlListener
	 * @see #removeAccessibleControlListener
	 */
	public void addAccessibleControlListener(AccessibleControlListener listener) {
		checkWidget();
		if (listener is null) DWT.error(__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
		accessibleControlListeners.addElement(listener);
	}

	/**
	 * Adds the listener to the collection of listeners who will
	 * be notifed when an accessible client asks for custom text control
	 * specific information. The listener is notified by sending it
	 * one of the messages defined in the <code>AccessibleTextListener</code>
	 * interface.
	 *
	 * @param listener the listener that should be notified when the receiver
	 * is asked for custom text control specific information
	 *
	 * @exception IllegalArgumentException <ul>
	 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
	 * </ul>
	 * @exception SWTException <ul>
	 *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
	 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
	 * </ul>
	 *
	 * @see AccessibleTextListener
	 * @see #removeAccessibleTextListener
	 *
	 * @since 3.0
	 */
	public void addAccessibleTextListener (AccessibleTextListener listener) {
		checkWidget ();
		if (listener is null) DWT.error(__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
		textListeners.addElement (listener);
	}

	/**
	 * Returns the control for this Accessible object.
	 *
	 * @return the receiver's control
	 * @since 3.0
	 */
	public Control getControl() {
		return control;
	}

	/**
	 * Invokes platform specific functionality to dispose an accessible object.
	 * <p>
	 * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
	 * API for <code>Accessible</code>. It is marked public only so that it
	 * can be shared within the packages provided by DWT. It is not
	 * available on all platforms, and should never be called from
	 * application code.
	 * </p>
	 */
	public void internal_dispose_Accessible() {
		if (iaccessible !is null)
			iaccessible.Release();
		iaccessible = null;
		Release();
	}

	/**
	 * Invokes platform specific functionality to handle a window message.
	 * <p>
	 * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
	 * API for <code>Accessible</code>. It is marked public only so that it
	 * can be shared within the packages provided by DWT. It is not
	 * available on all platforms, and should never be called from
	 * application code.
	 * </p>
	 */
	public int internal_WM_GETOBJECT (int wParam, int lParam) {
		if (objIAccessible is null) return 0;
		if (lParam == COM.OBJID_CLIENT) {
			return COM.LresultFromObject(&COM.IIDIAccessible, wParam, cast(IAccessible)objIAccessible);
		}
		return 0;
	}

	/**
	 * Removes the listener from the collection of listeners who will
	 * be notifed when an accessible client asks for certain strings,
	 * such as name, description, help, or keyboard shortcut.
	 *
	 * @param listener the listener that should no longer be notified when the receiver
	 * is asked for a name, description, help, or keyboard shortcut string
	 *
	 * @exception IllegalArgumentException <ul>
	 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
	 * </ul>
	 * @exception SWTException <ul>
	 *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
	 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
	 * </ul>
	 *
	 * @see AccessibleListener
	 * @see #addAccessibleListener
	 */
	public void removeAccessibleListener(AccessibleListener listener) {
		checkWidget();
		if (listener is null) DWT.error(__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
		accessibleListeners.removeElement(listener);
	}

	/**
	 * Removes the listener from the collection of listeners who will
	 * be notifed when an accessible client asks for custom control
	 * specific information.
	 *
	 * @param listener the listener that should no longer be notified when the receiver
	 * is asked for custom control specific information
	 *
	 * @exception IllegalArgumentException <ul>
	 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
	 * </ul>
	 * @exception SWTException <ul>
	 *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
	 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
	 * </ul>
	 *
	 * @see AccessibleControlListener
	 * @see #addAccessibleControlListener
	 */
	public void removeAccessibleControlListener(AccessibleControlListener listener) {
		checkWidget();
		if (listener is null) DWT.error(__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
		accessibleControlListeners.removeElement(listener);
	}

	/**
	 * Removes the listener from the collection of listeners who will
	 * be notifed when an accessible client asks for custom text control
	 * specific information.
	 *
	 * @param listener the listener that should no longer be notified when the receiver
	 * is asked for custom text control specific information
	 *
	 * @exception IllegalArgumentException <ul>
	 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
	 * </ul>
	 * @exception SWTException <ul>
	 *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
	 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
	 * </ul>
	 *
	 * @see AccessibleTextListener
	 * @see #addAccessibleTextListener
	 *
	 * @since 3.0
	 */
	public void removeAccessibleTextListener (AccessibleTextListener listener) {
		checkWidget ();
		if (listener is null) DWT.error(__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
		textListeners.removeElement (listener);
	}

	/**
	 * Sends a message to accessible clients that the child selection
	 * within a custom container control has changed.
	 *
	 * @exception SWTException <ul>
	 *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
	 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
	 * </ul>
	 *
	 * @since 3.0
	 */
	public void selectionChanged () {
		checkWidget();
		COM.NotifyWinEvent (COM.EVENT_OBJECT_SELECTIONWITHIN, control.handle, COM.OBJID_CLIENT, COM.CHILDID_SELF);
	}

	/**
	 * Sends a message to accessible clients indicating that the focus
	 * has changed within a custom control.
	 *
	 * @param childID an identifier specifying a child of the control
	 *
	 * @exception SWTException <ul>
	 *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
	 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
	 * </ul>
	 */
	public void setFocus(int childID) {
		checkWidget();
		COM.NotifyWinEvent (COM.EVENT_OBJECT_FOCUS, control.handle, COM.OBJID_CLIENT, childIDToOs(childID));
	}

	/**
	 * Sends a message to accessible clients that the text
	 * caret has moved within a custom control.
	 *
	 * @param index the new caret index within the control
	 *
	 * @exception SWTException <ul>
	 *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
	 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
	 * </ul>
	 *
	 * @since 3.0
	 */
	public void textCaretMoved (int index) {
		checkWidget();
		COM.NotifyWinEvent (COM.EVENT_OBJECT_LOCATIONCHANGE, control.handle, COM.OBJID_CARET, COM.CHILDID_SELF);
	}

	/**
	 * Sends a message to accessible clients that the text
	 * within a custom control has changed.
	 *
	 * @param type the type of change, one of <code>ACC.NOTIFY_TEXT_INSERT</code>
	 * or <code>ACC.NOTIFY_TEXT_DELETE</code>
	 * @param startIndex the text index within the control where the insertion or deletion begins
	 * @param length the non-negative length in characters of the insertion or deletion
	 *
	 * @exception SWTException <ul>
	 *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
	 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
	 * </ul>
	 *
	 * @see ACC#TEXT_INSERT
	 * @see ACC#TEXT_DELETE
	 *
	 * @since 3.0
	 */
	public void textChanged (int type, int startIndex, int length) {
		checkWidget();
		COM.NotifyWinEvent (COM.EVENT_OBJECT_VALUECHANGE, control.handle, COM.OBJID_CLIENT, COM.CHILDID_SELF);
	}

	/**
	 * Sends a message to accessible clients that the text
	 * selection has changed within a custom control.
	 *
	 * @exception SWTException <ul>
	 *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
	 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
	 * </ul>
	 *
	 * @since 3.0
	 */
	public void textSelectionChanged () {
		checkWidget();
		// not an MSAA event
	}

	HRESULT QueryInterface(REFIID riid, void ** ppvObject){
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;

		if (COM.IsEqualGUID(riid, &COM.IIDIUnknown)) {
			*ppvObject = cast(void*)(cast(IUnknown)objIAccessible);
			AddRef();
			return COM.S_OK;
		}

		if (COM.IsEqualGUID(riid, &COM.IIDIDispatch)) {
			*ppvObject = cast(void*)(cast(IDispatch)objIAccessible);
			AddRef();
			return COM.S_OK;
		}

		if (COM.IsEqualGUID(riid, &COM.IIDIAccessible)) {
			*ppvObject = cast(void*)(cast(IAccessible)objIAccessible);
			AddRef();
			return COM.S_OK;
		}

		if (COM.IsEqualGUID(riid, &COM.IIDIEnumVARIANT)) {
			*ppvObject = cast(void*)(cast(IEnumVARIANT)objIEnumVARIANT);
			AddRef();
			enumIndex = 0;
			return COM.S_OK;
		}

		int result = iaccessible.QueryInterface(riid, ppvObject);

		return result;
	}

	ULONG AddRef() {
		refCount++;
		return refCount;
	}

	ULONG Release() {
		refCount--;
		if (refCount == 0) {
//			if (objIAccessible !is null)
//				objIAccessible.dispose();
			objIAccessible = null;

//			if (objIEnumVARIANT !is null)
//				objIEnumVARIANT.dispose();
			objIEnumVARIANT = null;
		}
		return refCount;
	}

	HRESULT accDoDefaultAction(VARIANT varID) {
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
		// Currently, we don't let the application override this. Forward to the proxy.
		int code = iaccessible.accDoDefaultAction(varID);
		if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
		return code;
	}

	HRESULT accHitTest(LONG xLeft,  LONG yTop, VARIANT* pvarID) {
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
		if (accessibleControlListeners.getSize() == 0) {
			return iaccessible.accHitTest(xLeft, yTop, pvarID);
		}

		AccessibleControlEvent event = new AccessibleControlEvent(this);
		event.childID = ACC.CHILDID_NONE;
		event.x = xLeft;
		event.y = yTop;
		for (int i = 0; i < accessibleControlListeners.getSize(); i++) {
			AccessibleControlListener listener = cast(AccessibleControlListener)accessibleControlListeners.elementAt(i);
			listener.getChildAtPoint(event);
		}
		int childID = event.childID;
		if (childID == ACC.CHILDID_NONE) {
			return iaccessible.accHitTest(xLeft, yTop, pvarID);
		}
		short[] shortArr;
		shortArr ~= COM.VT_I4;
		COM.MoveMemory(pvarID, shortArr.ptr, 2);
		int[] intArr;
		intArr ~= childIDToOs(childID);
		COM.MoveMemory(pvarID + 8, intArr.ptr, 4);
		return COM.S_OK;
	}

	HRESULT accLocation(LONG* pxLeft, LONG* pyTop, LONG* pcxWidth, LONG* pcyHeight, VARIANT varID) {
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
		if ((varID.vt & 0xFFFF) != COM.VT_I4) return COM.E_INVALIDARG;

		/* Get the default location from the OS. */
		int osLeft = 0, osTop = 0, osWidth = 0, osHeight = 0;
		int code = iaccessible.accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varID);
		if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
		if (accessibleControlListeners.getSize() == 0) return code;
		if (code == COM.S_OK) {
			int[] pLeft = new int[1], pTop = new int[1], pWidth = new int[1], pHeight = new int[1];
			COM.MoveMemory(pLeft.ptr, pxLeft, 4);
			COM.MoveMemory(pTop.ptr, pyTop, 4);
			COM.MoveMemory(pWidth.ptr, pcxWidth, 4);
			COM.MoveMemory(pHeight.ptr, pcyHeight, 4);
			osLeft = pLeft[0]; osTop = pTop[0]; osWidth = pWidth[0]; osHeight = pHeight[0];
		}

		AccessibleControlEvent event = new AccessibleControlEvent(this);
		event.childID = osToChildID(varID.n1.n2.n3.lVal);
		event.x = osLeft;
		event.y = osTop;
		event.width = osWidth;
		event.height = osHeight;
		for (int i = 0; i < accessibleControlListeners.getSize(); i++) {
			AccessibleControlListener listener = cast(AccessibleControlListener)accessibleControlListeners.elementAt(i);
			listener.getLocation(event);
		}
		int[] intArr;
		intArr ~= event.x;
		COM.MoveMemory(pxLeft, intArr.ptr, 4);
		int[] intArr2;
		intArr2 ~= event.y;
		COM.MoveMemory(pyTop, intArr2.ptr, 4);
		int[] intArr3;
		intArr3 ~= event.width;
		COM.MoveMemory(pcxWidth, intArr3.ptr, 4);
		int[] intArr4;
		intArr4 ~= event.height;
		COM.MoveMemory(pcyHeight, intArr.ptr, 4);
		return COM.S_OK;

	}

	HRESULT accNavigate(LONG navDir, VARIANT varStart, VARIANT* pvarEnd) {
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
		// Currently, we don't let the application override this. Forward to the proxy.
		int code = iaccessible.accNavigate(navDir, varStart, pvarEnd);
		if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
		return code;
	}

	HRESULT accSelect(LONG flagsSelect, VARIANT varID) {
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
		// Currently, we don't let the application override this. Forward to the proxy.
		int code = iaccessible.accSelect(flagsSelect, varID);
		if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
		return code;
	}

	HRESULT get_accChild(VARIANT varChildID, LPDISPATCH * ppdispChild) {
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
		if ((varChildID.vt & 0xFFFF) != COM.VT_I4) return COM.E_INVALIDARG;
		if (accessibleControlListeners.getSize() == 0) {
			int code = iaccessible.get_accChild(varChildID, ppdispChild);
			if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
			return code;
		}

		AccessibleControlEvent event = new AccessibleControlEvent(this);
		event.childID = osToChildID(varChildID.n1.n2.n3.lVal);
		for (int i = 0; i < accessibleControlListeners.getSize(); i++) {
			AccessibleControlListener listener = cast(AccessibleControlListener)accessibleControlListeners.elementAt(i);
			listener.getChild(event);
		}
		Accessible accessible = event.accessible;
		if (accessible !is null) {
			*ppdispChild = accessible.objIAccessible;
			return COM.S_OK;
		}
		return COM.S_FALSE;

	}

	HRESULT get_accChildCount(LONG* pcountChildren) {
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;

		/* Get the default child count from the OS. */
		int osChildCount = 0;
		int code = iaccessible.get_accChildCount(pcountChildren);
		if (accessibleControlListeners.getSize() == 0) return code;
		if (code == COM.S_OK) {
			int[] pChildCount = new int[1];
			COM.MoveMemory(pChildCount.ptr, pcountChildren, 4);
			osChildCount = pChildCount[0];
		}

		AccessibleControlEvent event = new AccessibleControlEvent(this);
		event.childID = ACC.CHILDID_SELF;
		event.detail = osChildCount;
		for (int i = 0; i < accessibleControlListeners.getSize(); i++) {
			AccessibleControlListener listener = cast(AccessibleControlListener)accessibleControlListeners.elementAt(i);
			listener.getChildCount(event);
		}

		int[] intArr;
		intArr ~= event.detail;
		COM.MoveMemory(pcountChildren, intArr.ptr, 4);
		return COM.S_OK;

	}

	HRESULT get_accDefaultAction(VARIANT varID, BSTR* pszDefaultAction) {
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
		if ((varID.vt & 0xFFFF) != COM.VT_I4) return COM.E_INVALIDARG;

		/* Get the default defaultAction from the OS. */
		char[] osDefaultAction = null;
		int code = iaccessible.get_accDefaultAction(varID, pszDefaultAction);
		if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
		if (accessibleControlListeners.getSize() == 0) return code;
		if (code == COM.S_OK) {
			// The old SysString is freeed and bstr set to null
			osDefaultAction = Converter.BSTRToStr(*pszDefaultAction, true);
		}

		AccessibleControlEvent event = new AccessibleControlEvent(this);
		event.childID = osToChildID(varID.n1.n2.n3.lVal);
		event.result = osDefaultAction;
		for (int i = 0; i < accessibleControlListeners.getSize(); i++) {
			AccessibleControlListener listener = cast(AccessibleControlListener)accessibleControlListeners.elementAt(i);
			listener.getDefaultAction(event);
		}
		if (event.result is null) return code;
		wchar* ptr = COM.SysAllocString(Converter.StrToWCHARz(event.result));
		*pszDefaultAction = ptr;
		return COM.S_OK;

	}

	HRESULT get_accDescription(VARIANT varID, BSTR* pszDescription) {
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
		if ((varID.vt & 0xFFFF) != COM.VT_I4) return COM.E_INVALIDARG;

		/* Get the default description from the OS. */
		char[] osDescription = null;
		int code = iaccessible.get_accDescription(varID, pszDescription);
		if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
		if (accessibleListeners.getSize() == 0) return code;
		if (code == COM.S_OK) {
			int size = COM.SysStringByteLen(*pszDescription);
			if (size > 0) {
				size = (size + 1) /2;
				osDescription = Converter.WCHARzToStr(*pszDescription, size);
			}
		}

		AccessibleEvent event = new AccessibleEvent(this);
		event.childID = osToChildID(varID.n1.n2.n3.lVal);
		event.result = osDescription;
		for (int i = 0; i < accessibleListeners.getSize(); i++) {
			AccessibleListener listener = cast(AccessibleListener)accessibleListeners.elementAt(i);
			listener.getDescription(event);
		}
		if (event.result is null) return code;
		wchar* ptr = COM.SysAllocString(Converter.StrToWCHARz(event.result));
		COM.MoveMemory(pszDescription, &ptr, 4);
		return COM.S_OK;
	}

	HRESULT get_accFocus(VARIANT* pvarChild) {
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;

		/* Get the default focus child from the OS. */
		int osChild = ACC.CHILDID_NONE;
		int code = iaccessible.get_accFocus(pvarChild);
		if (accessibleControlListeners.getSize() == 0) return code;
		if (code == COM.S_OK) {
			if (pvarChild.vt == COM.VT_I4) {
				int[] pChild = new int[1];
				osChild = osToChildID(pvarChild.n1.n2.n3.lVal);
			}
		}

		AccessibleControlEvent event = new AccessibleControlEvent(this);
		event.childID = osChild;
		for (int i = 0; i < accessibleControlListeners.getSize(); i++) {
			AccessibleControlListener listener = cast(AccessibleControlListener)accessibleControlListeners.elementAt(i);
			listener.getFocus(event);
		}
		Accessible accessible = event.accessible;
		if (accessible !is null) {
			pvarChild.vt = COM.VT_DISPATCH;
			pvarChild.pdispVal = accessible.objIAccessible;
			return COM.S_OK;
		}
		int childID = event.childID;
		if (childID == ACC.CHILDID_NONE) {
			pvarChild.vt = COM.VT_EMPTY;
			return COM.S_FALSE;
		}
		if (childID == ACC.CHILDID_SELF) {
			pvarChild.vt = COM.VT_DISPATCH;
			pvarChild.pdispVal = objIAccessible;
			return COM.S_OK;
		}
		pvarChild.vt = COM.VT_I4;
		pvarChild.lVal = childIDToOs(childID);
		return COM.S_OK;
	}

	HRESULT get_accHelp(VARIANT varID, BSTR* pszHelp) {
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
		if ((varID.vt & 0xFFFF) != COM.VT_I4) return COM.E_INVALIDARG;

		/* Get the default help string from the OS. */
		char[] osHelp = null;
		int code = iaccessible.get_accHelp(varID, pszHelp);
		if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
		if (accessibleListeners.getSize() == 0) return code;
		if (code == COM.S_OK) {
			// the original SysString is clearuped and bstr set to null
			osHelp = Converter.BSTRToStr(*pszHelp, true);
		}

		AccessibleEvent event = new AccessibleEvent(this);
		event.childID = osToChildID(varID.n1.n2.n3.lVal);
		event.result = osHelp;
		for (int i = 0; i < accessibleListeners.getSize(); i++) {
			AccessibleListener listener = cast(AccessibleListener)accessibleListeners.elementAt(i);
			listener.getHelp(event);
		}
		if (event.result is null) return code;
		wchar* ptr = COM.SysAllocString(Converter.StrToWCHARz(event.result));
		*pszHelp = ptr;
		return COM.S_OK;
	}

	HRESULT get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild, LONG* pidTopic) {
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
		// Currently, we don't let the application override this. Forward to the proxy.
		int code = iaccessible.get_accHelpTopic(pszHelpFile, varChild, pidTopic);
		if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
		return code;
	}

	HRESULT get_accKeyboardShortcut(VARIANT varID, BSTR* pszKeyboardShortcut) {
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
		if ((varID.vt & 0xFFFF) != COM.VT_I4) return COM.E_INVALIDARG;

		/* Get the default keyboard shortcut from the OS. */
		char[] osKeyboardShortcut = null;
		int code = iaccessible.get_accKeyboardShortcut(varID, pszKeyboardShortcut);
		if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
		if (accessibleListeners.getSize() == 0) return code;
		if (code == COM.S_OK) {
			// the original SysString is clearuped and bstr set to null
			osKeyboardShortcut = Converter.BSTRToStr(*pszKeyboardShortcut, true);
		}

		AccessibleEvent event = new AccessibleEvent(this);
		event.childID = osToChildID(varID.lVal);
		event.result = osKeyboardShortcut;
		for (int i = 0; i < accessibleListeners.getSize(); i++) {
			AccessibleListener listener = cast(AccessibleListener)accessibleListeners.elementAt(i);
			listener.getKeyboardShortcut(event);
		}
		if (event.result is null) return code;
		wchar* ptr = COM.SysAllocString(Converter.StrToWCHARz(event.result));
		*pszKeyboardShortcut = ptr;
		return COM.S_OK;
	}

	HRESULT get_accName(VARIANT varID, BSTR* pszName) {
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
		if ((varID.vt & 0xFFFF) != COM.VT_I4) return COM.E_INVALIDARG;

		/* Get the default name from the OS. */
		char[] osName = null;
		int code = iaccessible.get_accName(varID, pszName);
		if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
		if (accessibleListeners.getSize() == 0) return code;
		if (code == COM.S_OK) {
			// the original SysString is clearuped and bstr set to null
			osName = Converter.BSTRToStr(*pszName, true);
		}

		AccessibleEvent event = new AccessibleEvent(this);
		event.childID = osToChildID(varID.lVal);
		event.result = osName;
		for (int i = 0; i < accessibleListeners.getSize(); i++) {
			AccessibleListener listener = cast(AccessibleListener)accessibleListeners.elementAt(i);
			listener.getName(event);
		}
		if (event.result is null) return code;
		wchar* ptr = COM.SysAllocString(Converter.StrToWCHARz(event.result));
		*pszName = ptr;
		return COM.S_OK;
	}

	HRESULT get_accParent(LPDISPATCH* ppdispParent) {
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
		// Currently, we don't let the application override this. Forward to the proxy.
		return iaccessible.get_accParent(ppdispParent);
	}

	HRESULT get_accRole(VARIANT varID, VARIANT* pvarRole) {
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
		if ((varID.vt & 0xFFFF) != COM.VT_I4) return COM.E_INVALIDARG;

		/* Get the default role from the OS. */
		int osRole = COM.ROLE_SYSTEM_CLIENT;
		int code = iaccessible.get_accRole(varID, pvarRole);
		if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
		// TEMPORARY CODE - process tree and table even if there are no apps listening
		if (accessibleControlListeners.getSize() == 0 && !(cast(Tree)control || cast(Table)control)) return code;
		if (code == COM.S_OK) {
			short[] pvt = new short[1];
			COM.MoveMemory(pvt.ptr, pvarRole, 2);
			if (pvt[0] == COM.VT_I4) {
				int[] pRole = new int[1];
				COM.MoveMemory(pRole.ptr, pvarRole + 8, 4);
				osRole = pRole[0];
			}
		}

		AccessibleControlEvent event = new AccessibleControlEvent(this);
		event.childID = osToChildID(varID.lVal);
		event.detail = osToRole(osRole);
		// TEMPORARY CODE
		/* Currently our checkbox table and tree are emulated using state mask
		 * images, so we need to specify 'checkbox' role for the items. */
		if (varID.lVal != COM.CHILDID_SELF) {
			if (cast(Tree)control || cast(Table)control) {
				if ((control.getStyle() & DWT.CHECK) != 0) event.detail = ACC.ROLE_CHECKBUTTON;
			}
		}
		for (int i = 0; i < accessibleControlListeners.getSize(); i++) {
			AccessibleControlListener listener = cast(AccessibleControlListener)accessibleControlListeners.elementAt(i);
			listener.getRole(event);
		}
		int role = roleToOs(event.detail);
		short[] shortArr;
		shortArr ~= COM.VT_I4;
		COM.MoveMemory(pvarRole, shortArr.ptr, 2);
		int[] intArr;
		intArr ~= role;
		COM.MoveMemory(pvarRole + 8, intArr.ptr, 4);
		return COM.S_OK;
	}

	HRESULT get_accSelection(VARIANT* pvarChildren) {
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;

		/* Get the default selection from the OS. */
		int osChild = ACC.CHILDID_NONE;
		int code = iaccessible.get_accSelection(pvarChildren);
		if (accessibleControlListeners.getSize() == 0) return code;
		if (code == COM.S_OK) {
			short[] pvt = new short[1];
			COM.MoveMemory(pvt.ptr, pvarChildren, 2);
			if (pvt[0] == COM.VT_I4) {
				int[] pChild = new int[1];
				COM.MoveMemory(pChild.ptr, pvarChildren + 8, 4);
				osChild = osToChildID(pChild[0]);
			} else if (pvt[0] == COM.VT_UNKNOWN) {
				osChild = ACC.CHILDID_MULTIPLE;
				/* Should get IEnumVARIANT from punkVal field... need better API here... */
			}
		}

		AccessibleControlEvent event = new AccessibleControlEvent(this);
		event.childID = osChild;
		for (int i = 0; i < accessibleControlListeners.getSize(); i++) {
			AccessibleControlListener listener = cast(AccessibleControlListener)accessibleControlListeners.elementAt(i);
			listener.getSelection(event);
		}
		Accessible accessible = event.accessible;
		if (accessible !is null) {
			pvarChildren.vt = COM.VT_DISPATCH;
			pvarChildren.pdispVal = accessible.objIAccessible;
			return COM.S_OK;
		}
		int childID = event.childID;
		if (childID == ACC.CHILDID_NONE) {
			pvarChildren.vt = COM.VT_EMPTY;
			return COM.S_FALSE;
		}
		if (childID == ACC.CHILDID_MULTIPLE) {
			pvarChildren.vt = COM.VT_UNKNOWN;

			/* Should return an IEnumVARIANT for this... so the next line is wrong... need better API here... */
			pvarChildren.punkVal = objIAccessible;
			return COM.S_OK;
		}
		if (childID == ACC.CHILDID_SELF) {
			pvarChildren.vt = COM.VT_DISPATCH;
			pvarChildren.pdispVal = objIAccessible;
			return COM.S_OK;
		}
		pvarChildren.vt = COM.VT_I4;
		pvarChildren.lVal = childIDToOs(childID);
		return COM.S_OK;
	}

	HRESULT get_accState(VARIANT varID, VARIANT* pvarState) {
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
		if ((varID.vt & 0xFFFF) != COM.VT_I4) return COM.E_INVALIDARG;

		/* Get the default state from the OS. */
		int osState = 0;
		int code = iaccessible.get_accState(varID, pvarState);
		if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
		// TEMPORARY CODE - process tree and table even if there are no apps listening
		if (accessibleControlListeners.getSize() == 0 && !(cast(Tree)control || cast(Table)control)) return code;
		if (code == COM.S_OK) {
			short[] pvt = new short[1];
			COM.MoveMemory(pvt.ptr, pvarState, 2);
			if (pvt[0] == COM.VT_I4) {
				int[] pState = new int[1];
				COM.MoveMemory(pState.ptr, pvarState + 8, 4);
				osState = pState[0];
			}
		}

		AccessibleControlEvent event = new AccessibleControlEvent(this);
		event.childID = osToChildID(varID.lVal);
		event.detail = osToState(osState);
		// TEMPORARY CODE
		/* Currently our checkbox table and tree are emulated using state mask
		 * images, so we need to determine if the item state is 'checked'. */
		if (varID.lVal != COM.CHILDID_SELF) {
			if (cast(Tree)control) {
				HWND hwnd = control.handle;
				TVITEM* tvItem = new TVITEM ();
				tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
				tvItem.stateMask = OS.TVIS_STATEIMAGEMASK;
				if (OS.COMCTL32_MAJOR >= 6) {
					tvItem.hItem = cast(HTREEITEM)OS.SendMessage (hwnd, OS.TVM_MAPACCIDTOHTREEITEM, varID.lVal, 0);
				} else {
					tvItem.hItem = cast(HTREEITEM)varID.lVal;
				}
				int result = OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, tvItem);
				int checked = (result != 0) && (((tvItem.state >> 12) & 1) == 0);
				if (checked) event.detail |= ACC.STATE_CHECKED;
			} else if (cast(Table)control) {
				Table table = cast(Table)control;
				TableItem item = table.getItem(event.childID);
				if (item !is null) {
					if (item.getChecked()) event.detail |= ACC.STATE_CHECKED;
				}
			}
		}
		for (int i = 0; i < accessibleControlListeners.getSize(); i++) {
			AccessibleControlListener listener = cast(AccessibleControlListener)accessibleControlListeners.elementAt(i);
			listener.getState(event);
		}
		int state = stateToOs(event.detail);
		short[] shortArr;
		shortArr ~= COM.VT_I4;
		COM.MoveMemory(pvarState, shortArr.ptr, 2);
		int[] intArr;
		intArr ~= state;
		COM.MoveMemory(pvarState + 8, intArr.ptr, 4);
		return COM.S_OK;
	}

	HRESULT get_accValue(VARIANT varID, BSTR* pszValue) {
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
		if ((varID.vt & 0xFFFF) != COM.VT_I4) return COM.E_INVALIDARG;

		/* Get the default value string from the OS. */
		char[] osValue = null;
		int code = iaccessible.get_accValue(varID, pszValue);
		if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
		if (accessibleControlListeners.getSize() == 0) return code;
		if (code == COM.S_OK) {
			int size = COM.SysStringByteLen(*pszValue);
			if (size > 0) {
				wchar[] buffer = new wchar[(size + 1) /2];
				COM.MoveMemory(buffer.ptr, *pszValue, size);
				osValue = Utf.toString16(buffer);
			}
		}

		AccessibleControlEvent event = new AccessibleControlEvent(this);
		event.childID = osToChildID(varID.lVal);
		event.result = osValue;
		for (int i = 0; i < accessibleControlListeners.getSize(); i++) {
			AccessibleControlListener listener = cast(AccessibleControlListener)accessibleControlListeners.elementAt(i);
			listener.getValue(event);
		}
		if (event.result is null) return code;
		wchar* ptr = COM.SysAllocString(Converter.StrToWCHARz(event.result));
		COM.MoveMemory(pszValue, &ptr, 4);
		return COM.S_OK;
	}

	HRESULT put_accName(VARIANT varID, BSTR* szName) {
		// MSAA: this method is no longer supported
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
		// We don't implement this. Forward to the proxy.
		int code = iaccessible.put_accName(varID, szName);
		if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
		return code;
	}

	HRESULT put_accValue(VARIANT varID, BSTR* szValue) {
		// MSAA: this method is typically only used for edit controls
		if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
		// We don't implement this. Forward to the proxy.
		int code = iaccessible.put_accValue(varID, szValue);
		if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
		return code;
	}

	/* IEnumVARIANT methods: Next, Skip, Reset */
	/* Retrieve the next celt items in the enumeration sequence.
	 * If there are fewer than the requested number of elements left
	 * in the sequence, retrieve the remaining elements.
	 * The number of elements actually retrieved is returned in pceltFetched
	 * (unless the caller passed in NULL for that parameter).
	 */
	HRESULT Next(ULONG celt, VARIANT *rgvar, ULONG *pceltFetched) {
		/* If there are no listeners, query the proxy for
		 * its IEnumVariant, and get the Next items from it.
		 */
		if (accessibleControlListeners.getSize() == 0) {
			IEnumVARIANT ienumvariant ;
			int code = iaccessible.QueryInterface(&COM.IIDIEnumVARIANT, cast(void**)&ienumvariant);
			if (code != COM.S_OK) return code;
			uint[] celtFetched = new uint[1];
			code = ienumvariant.Next(celt, rgvar, celtFetched.ptr);
			COM.MoveMemory(pceltFetched, celtFetched.ptr, 4);
			return code;
		}

		if (rgvar is null) return COM.E_INVALIDARG;
		if (pceltFetched is null && celt != 1) return COM.E_INVALIDARG;
		if (enumIndex == 0) {
			AccessibleControlEvent event = new AccessibleControlEvent(this);
			event.childID = ACC.CHILDID_SELF;
			for (int i = 0; i < accessibleControlListeners.getSize(); i++) {
				AccessibleControlListener listener = cast(AccessibleControlListener)accessibleControlListeners.elementAt(i);
				listener.getChildren(event);
			}
			variants = event.children;
		}
		Object[] nextItems = null;
		if (variants !is null && celt >= 1) {
			int endIndex = enumIndex + celt - 1;
			if (endIndex > (variants.length - 1)) endIndex = variants.length - 1;
			if (enumIndex <= endIndex) {
				nextItems = new Object[endIndex - enumIndex + 1];
				for (int i = 0; i < nextItems.length; i++) {
					Object child = variants[enumIndex];
					if (cast(Integer)child) {
						nextItems[i] = new Integer(childIDToOs((cast(Integer)child).intValue()));
					} else {
						nextItems[i] = child;
					}
					enumIndex++;
				}
			}
		}
		if (nextItems !is null) {
			for (int i = 0; i < nextItems.length; i++) {
				Object nextItem = nextItems[i];
				if (cast(Integer)nextItem) {
					int item = (cast(Integer) nextItem).intValue();
					short type = COM.VT_I4;
					COM.MoveMemory(rgvar + i * 16, &type, 2);
					COM.MoveMemory(rgvar + i * 16 + 8, &item, 4);
				} else {
					void* obj = cast(void*)((cast(Accessible) nextItem).objIAccessible);
					short type = COM.VT_DISPATCH;
					COM.MoveMemory(rgvar + i * 16, &type, 2);
					COM.MoveMemory(rgvar + i * 16 + 8, &obj, 4);
				}
			}
			if (pceltFetched !is null) {
				int[] intArr;
				intArr ~= nextItems.length;
				COM.MoveMemory(pceltFetched, intArr.ptr, 4);
			}
			if (nextItems.length == celt) return COM.S_OK;
		} else {
			if (pceltFetched !is null) {
				int[] intArr;
				intArr ~= 0;
				COM.MoveMemory(pceltFetched, intArr.ptr, 4);
			}
		}
		return COM.S_FALSE;
	}

	/* Skip over the specified number of elements in the enumeration sequence. */
	HRESULT Skip(ULONG celt)  {
		/* If there are no listeners, query the proxy
		 * for its IEnumVariant, and tell it to Skip.
		 */
		if (accessibleControlListeners.getSize() == 0) {
			IEnumVARIANT ienumvariant;
			int code = iaccessible.QueryInterface(&COM.IIDIEnumVARIANT, cast(void**)&ienumvariant);
			if (code != COM.S_OK) return code;
			code = ienumvariant.Skip(celt);
			return code;
		}

		if (celt < 1 ) return COM.E_INVALIDARG;
		enumIndex += celt;
		if (enumIndex > (variants.length - 1)) {
			enumIndex = variants.length - 1;
			return COM.S_FALSE;
		}
		return COM.S_OK;
	}

	/* Reset the enumeration sequence to the beginning. */
	HRESULT Reset() {
		/* If there are no listeners, query the proxy
		 * for its IEnumVariant, and tell it to Reset.
		 */
		if (accessibleControlListeners.getSize() == 0) {
			IEnumVARIANT ienumvariant;
			int code = iaccessible.QueryInterface(&COM.IIDIEnumVARIANT, cast(void**)&ienumvariant);
			if (code != COM.S_OK) return code;
			code = ienumvariant.Reset();
			return code;
		}

		enumIndex = 0;
		return COM.S_OK;
	}

	int childIDToOs(int childID) {
		if (childID == ACC.CHILDID_SELF) return COM.CHILDID_SELF;
		/*
		* Feature of Windows:
		* In Windows XP, tree item ids are 1-based indices. Previous versions
		* of Windows use the tree item handle for the accessible child ID.
		* For backward compatibility, we still take a handle childID for tree
		* items on XP. All other childIDs are 1-based indices.
		*/
		if (!(cast(Tree)control)) return childID + 1;
		if (OS.COMCTL32_MAJOR < 6) return childID;
		return OS.SendMessage (control.handle, OS.TVM_MAPHTREEITEMTOACCID, childID, 0);
	}

	int osToChildID(int osChildID) {
		if (osChildID == COM.CHILDID_SELF) return ACC.CHILDID_SELF;
		/*
		* Feature of Windows:
		* In Windows XP, tree item ids are 1-based indices. Previous versions
		* of Windows use the tree item handle for the accessible child ID.
		* For backward compatibility, we still take a handle childID for tree
		* items on XP. All other childIDs are 1-based indices.
		*/
		if (!(cast(Tree)control)) return osChildID - 1;
		if (OS.COMCTL32_MAJOR < 6) return osChildID;
		return OS.SendMessage (control.handle, OS.TVM_MAPACCIDTOHTREEITEM, osChildID, 0);
	}

	int stateToOs(int state) {
		int osState = 0;
		if ((state & ACC.STATE_SELECTED) != 0) osState |= COM.STATE_SYSTEM_SELECTED;
		if ((state & ACC.STATE_SELECTABLE) != 0) osState |= COM.STATE_SYSTEM_SELECTABLE;
		if ((state & ACC.STATE_MULTISELECTABLE) != 0) osState |= COM.STATE_SYSTEM_MULTISELECTABLE;
		if ((state & ACC.STATE_FOCUSED) != 0) osState |= COM.STATE_SYSTEM_FOCUSED;
		if ((state & ACC.STATE_FOCUSABLE) != 0) osState |= COM.STATE_SYSTEM_FOCUSABLE;
		if ((state & ACC.STATE_PRESSED) != 0) osState |= COM.STATE_SYSTEM_PRESSED;
		if ((state & ACC.STATE_CHECKED) != 0) osState |= COM.STATE_SYSTEM_CHECKED;
		if ((state & ACC.STATE_EXPANDED) != 0) osState |= COM.STATE_SYSTEM_EXPANDED;
		if ((state & ACC.STATE_COLLAPSED) != 0) osState |= COM.STATE_SYSTEM_COLLAPSED;
		if ((state & ACC.STATE_HOTTRACKED) != 0) osState |= COM.STATE_SYSTEM_HOTTRACKED;
		if ((state & ACC.STATE_BUSY) != 0) osState |= COM.STATE_SYSTEM_BUSY;
		if ((state & ACC.STATE_READONLY) != 0) osState |= COM.STATE_SYSTEM_READONLY;
		if ((state & ACC.STATE_INVISIBLE) != 0) osState |= COM.STATE_SYSTEM_INVISIBLE;
		if ((state & ACC.STATE_OFFSCREEN) != 0) osState |= COM.STATE_SYSTEM_OFFSCREEN;
		if ((state & ACC.STATE_SIZEABLE) != 0) osState |= COM.STATE_SYSTEM_SIZEABLE;
		if ((state & ACC.STATE_LINKED) != 0) osState |= COM.STATE_SYSTEM_LINKED;
		return osState;
	}

	int osToState(int osState) {
		int state = ACC.STATE_NORMAL;
		if ((osState & COM.STATE_SYSTEM_SELECTED) != 0) state |= ACC.STATE_SELECTED;
		if ((osState & COM.STATE_SYSTEM_SELECTABLE) != 0) state |= ACC.STATE_SELECTABLE;
		if ((osState & COM.STATE_SYSTEM_MULTISELECTABLE) != 0) state |= ACC.STATE_MULTISELECTABLE;
		if ((osState & COM.STATE_SYSTEM_FOCUSED) != 0) state |= ACC.STATE_FOCUSED;
		if ((osState & COM.STATE_SYSTEM_FOCUSABLE) != 0) state |= ACC.STATE_FOCUSABLE;
		if ((osState & COM.STATE_SYSTEM_PRESSED) != 0) state |= ACC.STATE_PRESSED;
		if ((osState & COM.STATE_SYSTEM_CHECKED) != 0) state |= ACC.STATE_CHECKED;
		if ((osState & COM.STATE_SYSTEM_EXPANDED) != 0) state |= ACC.STATE_EXPANDED;
		if ((osState & COM.STATE_SYSTEM_COLLAPSED) != 0) state |= ACC.STATE_COLLAPSED;
		if ((osState & COM.STATE_SYSTEM_HOTTRACKED) != 0) state |= ACC.STATE_HOTTRACKED;
		if ((osState & COM.STATE_SYSTEM_BUSY) != 0) state |= ACC.STATE_BUSY;
		if ((osState & COM.STATE_SYSTEM_READONLY) != 0) state |= ACC.STATE_READONLY;
		if ((osState & COM.STATE_SYSTEM_INVISIBLE) != 0) state |= ACC.STATE_INVISIBLE;
		if ((osState & COM.STATE_SYSTEM_OFFSCREEN) != 0) state |= ACC.STATE_OFFSCREEN;
		if ((osState & COM.STATE_SYSTEM_SIZEABLE) != 0) state |= ACC.STATE_SIZEABLE;
		if ((osState & COM.STATE_SYSTEM_LINKED) != 0) state |= ACC.STATE_LINKED;
		return state;
	}

	int roleToOs(int role) {
		switch (role) {
			case ACC.ROLE_CLIENT_AREA: return COM.ROLE_SYSTEM_CLIENT;
			case ACC.ROLE_WINDOW: return COM.ROLE_SYSTEM_WINDOW;
			case ACC.ROLE_MENUBAR: return COM.ROLE_SYSTEM_MENUBAR;
			case ACC.ROLE_MENU: return COM.ROLE_SYSTEM_MENUPOPUP;
			case ACC.ROLE_MENUITEM: return COM.ROLE_SYSTEM_MENUITEM;
			case ACC.ROLE_SEPARATOR: return COM.ROLE_SYSTEM_SEPARATOR;
			case ACC.ROLE_TOOLTIP: return COM.ROLE_SYSTEM_TOOLTIP;
			case ACC.ROLE_SCROLLBAR: return COM.ROLE_SYSTEM_SCROLLBAR;
			case ACC.ROLE_DIALOG: return COM.ROLE_SYSTEM_DIALOG;
			case ACC.ROLE_LABEL: return COM.ROLE_SYSTEM_STATICTEXT;
			case ACC.ROLE_PUSHBUTTON: return COM.ROLE_SYSTEM_PUSHBUTTON;
			case ACC.ROLE_CHECKBUTTON: return COM.ROLE_SYSTEM_CHECKBUTTON;
			case ACC.ROLE_RADIOBUTTON: return COM.ROLE_SYSTEM_RADIOBUTTON;
			case ACC.ROLE_COMBOBOX: return COM.ROLE_SYSTEM_COMBOBOX;
			case ACC.ROLE_TEXT: return COM.ROLE_SYSTEM_TEXT;
			case ACC.ROLE_TOOLBAR: return COM.ROLE_SYSTEM_TOOLBAR;
			case ACC.ROLE_LIST: return COM.ROLE_SYSTEM_LIST;
			case ACC.ROLE_LISTITEM: return COM.ROLE_SYSTEM_LISTITEM;
			case ACC.ROLE_TABLE: return COM.ROLE_SYSTEM_TABLE;
			case ACC.ROLE_TABLECELL: return COM.ROLE_SYSTEM_CELL;
			case ACC.ROLE_TABLECOLUMNHEADER: return COM.ROLE_SYSTEM_COLUMNHEADER;
			case ACC.ROLE_TABLEROWHEADER: return COM.ROLE_SYSTEM_ROWHEADER;
			case ACC.ROLE_TREE: return COM.ROLE_SYSTEM_OUTLINE;
			case ACC.ROLE_TREEITEM: return COM.ROLE_SYSTEM_OUTLINEITEM;
			case ACC.ROLE_TABFOLDER: return COM.ROLE_SYSTEM_PAGETABLIST;
			case ACC.ROLE_TABITEM: return COM.ROLE_SYSTEM_PAGETAB;
			case ACC.ROLE_PROGRESSBAR: return COM.ROLE_SYSTEM_PROGRESSBAR;
			case ACC.ROLE_SLIDER: return COM.ROLE_SYSTEM_SLIDER;
			case ACC.ROLE_LINK: return COM.ROLE_SYSTEM_LINK;
			default : break;
		}
		return COM.ROLE_SYSTEM_CLIENT;
	}

	int osToRole(int osRole) {
		int role = COM.ROLE_SYSTEM_CLIENT;
		switch (osRole) {
			case COM.ROLE_SYSTEM_CLIENT: return ACC.ROLE_CLIENT_AREA;
			case COM.ROLE_SYSTEM_WINDOW: return ACC.ROLE_WINDOW;
			case COM.ROLE_SYSTEM_MENUBAR: return ACC.ROLE_MENUBAR;
			case COM.ROLE_SYSTEM_MENUPOPUP: return ACC.ROLE_MENU;
			case COM.ROLE_SYSTEM_MENUITEM: return ACC.ROLE_MENUITEM;
			case COM.ROLE_SYSTEM_SEPARATOR: return ACC.ROLE_SEPARATOR;
			case COM.ROLE_SYSTEM_TOOLTIP: return ACC.ROLE_TOOLTIP;
			case COM.ROLE_SYSTEM_SCROLLBAR: return ACC.ROLE_SCROLLBAR;
			case COM.ROLE_SYSTEM_DIALOG: return ACC.ROLE_DIALOG;
			case COM.ROLE_SYSTEM_STATICTEXT: return ACC.ROLE_LABEL;
			case COM.ROLE_SYSTEM_PUSHBUTTON: return ACC.ROLE_PUSHBUTTON;
			case COM.ROLE_SYSTEM_CHECKBUTTON: return ACC.ROLE_CHECKBUTTON;
			case COM.ROLE_SYSTEM_RADIOBUTTON: return ACC.ROLE_RADIOBUTTON;
			case COM.ROLE_SYSTEM_COMBOBOX: return ACC.ROLE_COMBOBOX;
			case COM.ROLE_SYSTEM_TEXT: return ACC.ROLE_TEXT;
			case COM.ROLE_SYSTEM_TOOLBAR: return ACC.ROLE_TOOLBAR;
			case COM.ROLE_SYSTEM_LIST: return ACC.ROLE_LIST;
			case COM.ROLE_SYSTEM_LISTITEM: return ACC.ROLE_LISTITEM;
			case COM.ROLE_SYSTEM_TABLE: return ACC.ROLE_TABLE;
			case COM.ROLE_SYSTEM_CELL: return ACC.ROLE_TABLECELL;
			case COM.ROLE_SYSTEM_COLUMNHEADER: return ACC.ROLE_TABLECOLUMNHEADER;
			case COM.ROLE_SYSTEM_ROWHEADER: return ACC.ROLE_TABLEROWHEADER;
			case COM.ROLE_SYSTEM_OUTLINE: return ACC.ROLE_TREE;
			case COM.ROLE_SYSTEM_OUTLINEITEM: return ACC.ROLE_TREEITEM;
			case COM.ROLE_SYSTEM_PAGETABLIST: return ACC.ROLE_TABFOLDER;
			case COM.ROLE_SYSTEM_PAGETAB: return ACC.ROLE_TABITEM;
			case COM.ROLE_SYSTEM_PROGRESSBAR: return ACC.ROLE_PROGRESSBAR;
			case COM.ROLE_SYSTEM_SLIDER: return ACC.ROLE_SLIDER;
			case COM.ROLE_SYSTEM_LINK: return ACC.ROLE_LINK;
			default : break;
		}
		return role;
	}

	/* checkWidget was copied from Widget, and rewritten to work in this package */
	void checkWidget () {
		if (!isValidThread ()) DWT.error(__FILE__, __LINE__, DWT.ERROR_THREAD_INVALID_ACCESS);
		if (control.isDisposed ()) DWT.error(__FILE__, __LINE__, DWT.ERROR_WIDGET_DISPOSED);
	}

	/* isValidThread was copied from Widget, and rewritten to work in this package */
	boolean isValidThread () {
		return cast(boolean)(control.getDisplay ().getThread () is Thread.getThis ());
	}

}

